import * as THREE from 'three'

/**
 * 根据给定的多边形和扩展系数，创建一个新的多边形
 * @param polygon 多边形点位数据
 * @param expand 外扩距离
 * @returns 新多边形
 */
function createNewPolygon(polygon: [number, number][], expand: number): [number, number][] {
  let newPolygon: [number, number][] = []; // 新的多边形
  let len = polygon.length; // 多边形的顶点数量
  if (len < 3) return polygon; // 如果顶点数量少于3，则直接返回
  let flag = 0; // 用来记录需要反向外扩的顶点的数量

  // 遍历多边形的每一个顶点
  for (let i = 0; i < len; i++) {
    const p = polygon[i]; // 当前顶点
    const p1 = polygon[i === 0 ? len - 1 : i - 1]; // 当前顶点的前一个顶点
    const p2 = polygon[i === len - 1 ? 0 : i + 1]; // 当前顶点的后一个顶点

    // 计算两个向量，分别指向前一个和后一个顶点
    let v1x = p1[0] - p[0];
    let v1y = p1[1] - p[1];
    let n1 = norm(v1x, v1y);
    let vv1x = v1x / n1; // 单位化，使长度为1
    let vv1y = v1y / n1;

    let v2x = p2[0] - p[0];
    let v2y = p2[1] - p[1];
    let n2 = norm(v2x, v2y);
    let vv2x = v2x / n2;
    let vv2y = v2y / n2;

    // 计算扩展长度
    let vectorLen = -expand / Math.sqrt((1 - (vv1x * vv2x + vv1y * vv2y)) / 2.0);
    let judge = v1x * v2y - v2x * v1y;

    // 如果需要反向扩展，更新扩展长度和flag
    if (judge < 0) vectorLen *= -1;
    if (judge < 0) flag++;

    let vx = vv1x + vv2x;
    let vy = vv1y + vv2y;
    vectorLen = vectorLen / norm(vx, vy);
    vx *= vectorLen;
    vy *= vectorLen;

    // 添加新的顶点到新的多边形上
    newPolygon.push([vx + p[0], vy + p[1]]);
  }

  // 如果所有顶点都需要反向外扩，则整体进行反向扩展
  if (flag === len) {
    newPolygon = []
    for (let i = 0; i < len; i++) {
      const p = polygon[i];
      const p1 = polygon[i === 0 ? len - 1 : i - 1];
      const p2 = polygon[i === len - 1 ? 0 : i + 1];

      // 与上面的步骤类似
      let v1x = p1[0] - p[0];
      let v1y = p1[1] - p[1];
      let n1 = norm(v1x, v1y);
      let vv1x = v1x / n1;
      let vv1y = v1y / n1;

      let v2x = p2[0] - p[0];
      let v2y = p2[1] - p[1];
      let n2 = norm(v2x, v2y);
      let vv2x = v2x / n2;
      let vv2y = v2y / n2;

      let vectorLen = -expand / Math.sqrt((1 - (vv1x * vv2x + vv1y * vv2y)) / 2.0);
      let vx = vv1x + vv2x;
      let vy = vv1y + vv2y;
      vectorLen = vectorLen / norm(vx, vy);
      vx *= vectorLen;
      vy *= vectorLen;

      newPolygon.push([vx + p[0], vy + p[1]]);
    }
  }
  return newPolygon; // 返回新的多边形
}

/**计算二维向量的模 */
function norm(x: number, y: number): number {
  return Math.sqrt(x * x + y * y);
}

/**
 * 二维坐标数据转THREE.Vector3[]数据
 * @param points 二维坐标数据
 * @param closed 是否闭合
 */
function pointListToV3(points: [number, number][], closed = false, height = 0) {
  let linePoints = []
  for (let i = 0; i < points.length; i++) {
    linePoints.push(
      new THREE.Vector3(points[i][0], height, points[i][1])
    )
  }
  if (closed) {
    linePoints.push(
      new THREE.Vector3(points[0][0], height, points[0][1])
    )
  }
  return linePoints
}

/**
 * 流光
 * @param pointList 二维坐标点位数据
 * @param lineTexture 流光图片
 */
function getFlyCurve(options: {
  points: [number, number][]
  size: number,
  repeatX: number
}) {
  const vector3List = pointListToV3(options.points)
  const lineTexture = new THREE.TextureLoader().load('images/line3.png', (mapTexture) => {
    //每个都重复
    mapTexture.wrapS = mapTexture.wrapT = THREE.RepeatWrapping
    mapTexture.repeat.set(options.repeatX, 1)
    mapTexture.needsUpdate = true
  })
  let lineMaterial = new THREE.MeshBasicMaterial({
    map: lineTexture,
    side: THREE.DoubleSide,
    transparent: true
  })
  const curvePath = new THREE.CatmullRomCurve3(vector3List) // 曲线路径
  let geometry = new THREE.TubeGeometry(curvePath, 64, options.size, 8, false)
  let lineMesh = new THREE.Mesh(geometry, lineMaterial)
  lineMesh.position.y = options.size / 2
  return {
    lineMesh,
    lineTexture
  }
}

/**计算两点之间按distance添加新点位 */
function generatePoints(points: THREE.Vector3[], distance: number) : THREE.Vector3[] {
  const result: THREE.Vector3[] = [];

  for(let i = 0; i < points.length - 1; i++) {
    const start = points[i];
    const end = points[i + 1];
    const segmentLength = start.distanceTo(end);
    const steps = Math.floor(segmentLength / distance);

    // Start point
    result.push(start);

    // New points
    for(let j = 1; j < steps; j++) {
      const t = j / steps;
      const newPoint = new THREE.Vector3().lerpVectors(start, end, t);
      result.push(newPoint);	
    }
  }

  // Add last point of the inputArray
  result.push(points[points.length - 1]);

  return result;
}

export {
  createNewPolygon,
  pointListToV3,
  getFlyCurve,
  generatePoints
}